package com.zhao.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import com.zhao.po.Car;
import com.zhao.po.Product;
import com.zhao.po.vo.OrderItemDetail;

/**
 * 贪心算法－背包算法： 背包算法是从一堆里面选择出最多能容纳的。 我们要
 */
public class BackPackageUtil {

	private BigDecimal[] weights;// 物品重量

	private BigDecimal[] values;// 物品体积

	private Car car; // 要装车的汽车

	private List<OrderItemDetail> items; // 要装车的订单明细

	private List<Product> products; // 订单明细中的商品

	private int[] tags;

	private int size; // 订单中的商品的数量

	public BackPackageUtil(Car car, List<OrderItemDetail> orderItems) {
		super();
		this.car = car;
		this.items = orderItems;

		for (OrderItemDetail item : items) {
			for (int i = 0; i < item.getProductcount(); i++) {
				products.add(item.getProduct());
			}
		}

		size = this.products.size();
		// 初始化属性的值
		this.weights = new BigDecimal[size];
		this.values = new BigDecimal[size];

		for (int i = 0; i < size; i++) {
			Product product = products.get(i);
			this.weights[i] = product.getProductwith();
			this.values[i] = product.getProductheight().multiply(product.getProductlenth())
					.multiply(product.getProductwith());
		}

		// step1:求物品性价比－》并从大到小排序
		// 这里需要考虑怎么计算性价比

		BigDecimal[] prices = new BigDecimal[size];// 每个物品的性价比(每kg的价值)

		// 用一个数组用于保存排序后的性价比和最开始的物品重量的下标
		tags = new int[size];
		for (int i = 0; i < size; i++) {
			prices[i] = values[i].divide(weights[i]);
			tags[i] = i;// 默认排序
		}

		// 选择排序
		for (int i = 0; i < size; i++) {
			for (int j = i + 1; j < size; j++) {
				if (-1 == prices[i].compareTo(prices[j])) {
					// 交换
					BigDecimal temp = prices[i];
					prices[i] = prices[j];
					prices[j] = temp;

					int tag = tags[i];
					tags[i] = tags[j];
					tags[j] = tag;
				}
			}
		}

	}

	/**
	 * 根据物品重量和物品价值，求出每个物品的性价比－》将性价比进行排序－》选取性价比最高的物品添加到背包中－》直到背包不能再添加
	 */
	// 返回能装车的订单明细
	public List<OrderItemDetail> backpack() {

		// 定义用来装载能直接返回的订单明细
		List<OrderItemDetail> backList = new ArrayList<OrderItemDetail>();

		// 车辆的剩余容量
		BigDecimal capacity = car.getMaxweight().subtract(car.getHasweight());
		// 根据已经从大到小排好序的性价比，和相对应的重量和价值，添加到背包中
		for (int i = 0; i < size; i++) {

			// 根据tags数组中重量的下标，拿到重量
			if (-1 == weights[tags[i]].compareTo(capacity)) {

				// 这里怎么将商品添加到新的集合? 怎么获取对应的item对象。 在这里我们能拿到的就是i
				Product item = this.products.get(i);

				// 这里还要加上一个判断： 判断这个商品是否能添加进车，这里需要做一个判断
				if (canFillItem(item)) {
					products.remove(item);

				}

				capacity = capacity.subtract(weights[tags[i]]);
			}
		}

		// 如何删除对应的订单明细中的存储量,且当容量减少为0后删除对应的订单明细

		// 这里需要返回的是未能装车的订单明细
		// 在遍历之前先将订单明细中的所有商品数变成0;
		for (Product product : products) {
			// 遍历剩余的商品，如果在原来的订单明细中找到与之相等的，订单明细中的商品容量先
			for (OrderItemDetail item : items) {
				if (product.equals(item.getProduct())) {
					if (backList.contains(item)) {
						item.setProductcount(item.getProductcount() + 1);
					} else {
						// 重新添加商品，且设置商品容量为0
						backList.add(item);
						item.setProductcount(0);
					}
				}
			}

		}

		return backList;
	}

	/**
	 * 判断这个商品能否装车
	 * 
	 * @return
	 */
	private Boolean canFillItem(Product product) {
		/**
		 * 判断这个商品是否能够装车， 1： 看车是否能够装下 2：看车辆的剩余量是否够
		 */
		BigDecimal itemMax = maxOfThree(product.getProductheight(), product.getProductlenth(),
				product.getProductwith());
		BigDecimal carMax = maxOfThree(this.car.getCarheight(), this.car.getCarlength(), this.car.getCarwith());
		BigDecimal pvolume = product.getProductheight().multiply(product.getProductlenth())
				.multiply(product.getProductweight());
		BigDecimal cvolume = car.getCarheight().multiply(car.getCarlength()).multiply(car.getCarwith());
		if (itemMax.compareTo(carMax) == -1) {
			// 判断容量
			if (car.getMaxweight().add(product.getProductweight()).compareTo(car.getMaxweight()) == -1
					&& car.getHascontain().add(pvolume).compareTo(cvolume) == -1) {
				return true;
			}
		}
		return false;
	}

	private BigDecimal maxOfThree(BigDecimal a, BigDecimal b, BigDecimal c) {
		BigDecimal max;
		if (a.compareTo(b) == 1 && a.compareTo(c) == 1) {
			max = a;
		} else if (c.compareTo(a) == 1 && c.compareTo(b) == 1) {
			max = c;
		} else {
			max = b;
		}
		return max;
	}

}
